/*
 * keyboard.c - Mokri
 * Contributors: xMine
 * (Any new Contributor may add himself/herself to the list)
 */

#include "keyboard.h"

/*** Funktionstasten ***/

static BOOL key_lshift = FALSE;
static BOOL key_rshift = FALSE;
static BOOL key_lalt = FALSE;
static BOOL key_ralt = FALSE;
static BOOL key_lctrl = FALSE;
static BOOL key_rctrl = FALSE;
static BOOL key_lwin = FALSE;
static BOOL key_rwin = FALSE;
static BOOL key_context = FALSE;
static BOOL key_capslock = FALSE;
static BOOL key_numlock = FALSE;
static BOOL key_scrolllock = FALSE;

/*** LEDs ***/
static BOOL led_numlock = FALSE;
static BOOL led_capslock = FALSE;
static BOOL led_scrolllock = FALSE;

struct keyb_listener listener[256];

/*** Tastatur Layouts ***/

// 0x0 = Error/Unbenutzt, 0x01 = Escape, 0x02 = Sonstige Funktionstaste
static char maske_normal [0x60] = {
	0x0 /* Error */, 0x1 /* Esc */,
	'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0xe1 /* ß */, 0x27 /* ´ */,
	'\b' /* Backspace */,
	'\t' /* Tab */, 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', 0x81 /* ü */, '+',
	'\n' /* Enter */, 0x2 /* lctrl */,
	'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x94 /* ö */, 0x84 /* ä */, '#',
	0x2 /* lshift */,
	'#', 'y', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '-', 0x2 /* rshift */,
	0x2 /* Druck */, 0x2 /* lalt */, ' ' /* space */, 0x2 /* caps */,
	0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, /* F1 - F10 */
	0x2 /* numlock */, 0x2 /* scrolllock */,
	/* Numpad */
	'7', '8', '9', '-',
	'4', '5', '6', '+',
	'1', '2', '3',
	'0', ',',
	0x0 /* ??? alt-sysrq */, 0x0 /* ??? */,
	'<', 0x2 /* F11 */, 0x2 /* F12 */, 0x0, 0x0,
	0x2 /* lwin */, 0x2 /* rwin */, 0x2 /* context */
};

// Zeichen für Tasten, die bei Shift etwas anderes ausgeben (ausgenommen Buchstaben)
static char maske_shift [0x60] = {
	0x0, 0x0, /* 0x0 = Keine manuelle Änderung */
	'!', '"', 0x0, '$', '%', '&', '/', '(', ')', '=', '?', '`',
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9a /* ü */, 0x0, 0x0,
	0x0 /* Enter */, 0x0 /* lctrl */,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x99 /* ö */, 0x8e /* ä */, '*',
	0x0,  /* lshift */
	'\'', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  ';', ':', '_', 0x0 /* rshift */,
	0x0 /* Druck */, 0x0 /* lalt */, 0x0 /* space */, 0x0 /* caps */,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* F1 - F10 */
	0x0 /* numlock */, 0x0 /* scrolllock */,
	/* Numpad */
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0 /* ??? alt-sysrq */, 0x0 /* ??? */,
	'>', 0x0 /* F11 */, 0x0 /* F12 */, 0x0, 0x0,
	0x0 /* lwin */, 0x0 /* rwin */, 0x0 /* context */
};

// Zeichen Mods für AltGr
static char maske_ralt [0x60] = {
	0x0, 0x0, /* 0x0 = Keine manuelle Änderung */
	0x0, 0xab, 0x0, 0xac, 0x0, 0x0, '{', '[', ']', '}', '\\', 0x0,
	0x0, 0x0, '@', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8d, 0x0, 0x0, 0x0, 0x0,
	0x0 /* Enter */, 0x0 /* lctrl */,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x0 /* ö */, 0x0 /* ä */, '*',
	0x0,  /* lshift */
	'\'', 0xaf, 0xae, 0x0, 0x0, 0x0, 0x0, 0x0,  0xf9, 0xfa, '_', 0x0 /* rshift */,
	0x0 /* Druck */, 0x0 /* lalt */, 0x0 /* space */, 0x0 /* caps */,
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* F1 - F10 */
	0x0 /* numlock */, 0x0 /* scrolllock */,
	/* Numpad */
	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
	0x0 /* ??? alt-sysrq */, 0x0 /* ??? */,
	0x7c, 0x0 /* F11 */, 0x0 /* F12 */, 0x0, 0x0,
	0x0 /* lwin */, 0x0 /* rwin */, 0x0 /* context */
};

/* Startet die Tastatur
 * Parameter: Keine
 * Rückgaben: Keine
 */
void keyboard_init () {
	send_command (0xF4); // Tastatur aktivieren
	memset (listener, 0x0, sizeof (struct keyb_listener)*256);
	// Intervall setzen
	send_command (0xF3); // Wir wollen den Intervall ändern
	send_command (0b01111111);
}

/* Liest den Befehlspuffer leer und sendet dann einen Befehl an die Tastatur
 * Parameter: Kommandobyte
 * Rückgaben: Keine
 */
void send_command (uint8_t command) {
	while ((inb(0x64) & 0x2)) { } // Puffer leerlesen
	outb (0x60, command);
}

/* Funktionen um die LEDs auf der Tastatur ein- und auszuschalten
 * Parameter: Ein oder Aus
 * Rückgaben: Keine
 */
void set_led_numlock (BOOL active) {
	led_numlock = active;
	send_command (0xED);
	send_command (get_led_mask());
}

void set_led_capslock (BOOL active) {
	led_capslock = active;
	send_command (0xED);
	send_command (get_led_mask());
}

void set_led_scrolllock (BOOL active) {
	led_scrolllock = active;
	send_command (0xED);
	send_command (get_led_mask());
}

void set_leds (BOOL num, BOOL caps, BOOL scroll) {
	led_numlock = num;
	led_capslock = caps;
	led_scrolllock = scroll;

	send_command (0xED);
	send_command (get_led_mask());
}

/* Liefert die Bitmaske für die LEDs zurück
 * Parameter: Keine
 * Rückgaben: Bitmaske
 */
uint8_t get_led_mask () {
	uint8_t ret = 0;
	if (led_numlock) {
		ret |= 2;
	}
	if (led_capslock) {
		ret |= 4;
	}
	if (led_scrolllock) {
		ret |= 1;
	}

	return ret;
}

/* Wird aufgerufen, wenn eine Taste betätigt wird
 * Paramter: Keine
 * Rückgaben: Keine
 */
void keyboard_handler () {
	uint8_t scancode;
	char zeichen;
	static BOOL e0code = FALSE;
	BOOL pressed = TRUE;

	uint8_t status = inb (0x64);
	if (!(status & 0x01)/* || (status &(1 << 5))*/ /* Maus */) {
		return;
	}
	
	scancode = inb(0x60); // Scancode von der Tastatur einlesen
	
	if (scancode == 0xE0) { // Erweiterter Bla?
		e0code = TRUE;
		return;
	}
	
	// Fake Shift kicken
	if (e0code && (scancode == 0x2A || scancode == 0x36)) {
		e0code = FALSE;
		return;
	}
	
	if (scancode & 0x80) { // Wurde die Taste losgelassen?
		pressed = FALSE;
		scancode &= ~0x80;
	}
	
	zeichen = maske_normal[scancode];
	if (zeichen == 0x02) { // Funktionstaste?
		BOOL gefunden = TRUE;
		
		switch (scancode) {
		case 0x1D:
			if (e0code) {
				key_rctrl = pressed;
			} else {
				key_lctrl = pressed;
			}
			break;
		case 0x2A:
			key_lshift = pressed;
			break;
		case 0x36:
			key_rshift = pressed;
			break;
		case 0x38:
			if (e0code) {
				key_ralt = pressed;
			} else {
				key_lalt = pressed;
			}
			break;
		case 0x3A:
			key_capslock = pressed;
			break;
		case 0x45:
			key_numlock = pressed;
			break;
		case 0x46:
			key_scrolllock = pressed;
			break;
		case 0x5B:
			key_lwin = pressed;
			break;
		case 0x5C:
			key_rwin = pressed;
			break;
		case 0x5D:
			if (e0code) {
				key_context = pressed;
			}
			break;
		default:
			gefunden = FALSE;
			break;
		}
		
		if (gefunden) {
			zeichen = 0x0;
		}
	}

	e0code = FALSE;

	// Tasten kombos
	if ( (key_lshift || key_rshift) && zeichen > 0x03) { // Wenn shift gedrückt wird
		if (maske_shift[scancode] != 0x0) { // Besondere Belegung beachten
			zeichen = maske_shift[scancode];
		} else {
			if (zeichen >= 'a' && zeichen <= 'z') {
				zeichen &= ~32;
			}
		}
	}

	if (key_ralt && zeichen > 0x03) {
		if (maske_ralt[scancode] != 0x0) {
			zeichen = maske_ralt[scancode];
		}
	}

	if (zeichen == 0x0) {
		return;
	}
	
	if (zeichen < 0x03) {
		zeichen = 0x0;
	}

	// Struct zusammen basteln
	struct keyboard_struct keyb = {
		.input = zeichen,
		.code = scancode,
		.key_lshift = key_lshift,
		.key_rshift = key_rshift,
		.key_lalt = key_lalt,
		.key_ralt = key_ralt,
		.key_lctrl = key_lctrl,
		.key_rctrl = key_rctrl,
		.key_lwin = key_lwin,
		.key_rwin = key_rwin,
		.key_context = key_context,
		.key_capslock = key_capslock,
		.key_numlock = key_numlock,
		.key_scrolllock = key_scrolllock,
		.pressed = pressed,
	};

	// Event listener aufrufen
	keyb_send_event (&keyb);

}

/* Ruft alle Handler Funktionen auf
 * Parameter: Pointer zu einer keyboard struktur
 * Rückgaben: Keine
 */
void keyb_send_event (struct keyboard_struct *keyb) {
	int i;
	for (i=0; i<256; i++) {
		if (listener[i].function != NULL) {
			listener[i].function (keyb);
		}
	}
}

/* Trägt eine Handlerfunktion in die Liste ein
 * Paramter: Pointer zur Handler Funktion
 * Rückgaben: Keine
 */
void keyb_add_handler (void (*func)(struct keyboard_struct *)) {
	int i;
	for (i=0; i<256; i++) {
		if (listener[i].function == NULL) {
			listener[i].function = func;
			return;
		}
	}
}

/* Löscht eine Handlerfunktion aus der Liste
 * Parameter: Pointer zur Handler Funktion
 * Rückgaben: Keine
 */
void keyb_remove_handler (void (*func)(struct keyboard_struct *)) {
	int i;
	for (i=0; i<256; i++) {
		if (listener[i].function == func) {
			listener[i].function = NULL;
		}
	}
}
